#!/bin/bash
# Project: bash_tools
# Filename: vector.lib.sh
# Author: Wojciech Ornowski
# Date: 2012-06-14
# Version: 1.0
# License: 
#
# Describe: Library contains vector functionality. BUMMER: "expr index" have been used to count string characters - count on "special Polish" characters is incorrect.
#	    Using this characters in alphanumeric vector fields is not allowed. 	
#
# Usage: Library. Add vector library to your code using ". vector.lib.sh"
#
# Library postfix:  _937_vec - this is to ensure artificial encapsulation.

#VARIABLES
vectors_937_vec=()
names_937_vec=()
next_937_vec=()
len_937_vec=()
INDEX_937_vec=0
separator_937_vec=`echo -e "\x16"`

# FUNCTION
# Name: printHelp_937_vec()
# Describe: Help function for library
# Usage: printHelp_937_vec - no arguments
# Exitcodes: -
printHelp_937_vec()
{
  echo ""
  echo "Library: vector.lib.sh"
  echo "	Public: vec_new() vec_add() vec_get() vec_set() vec_getall() vec_getid vec_contain() vec_len()"
  echo "		vec_shift() vec_clone() vec_del()"
  echo "	Private: printHelp_937_vec() exist_937_vec()"
  echo ""
  echo "	Library postfix: _937_vec"
}


if [ $# == 2 ] && [ $1 == "-lib" ] && [ $2 == "vector.lib.sh" ]; then
    printHelp_937_vec
    exit 0
fi

# FUNCTION
# Name: exist_937_vec()
# Describe: Private function. Checks if vector name already exists.
# Usage: exist_937_vec name
# Exitcodes: 0 if vector exist
exist_937_vec()
{
  INDEX_937_vec=0
  if ! [ $# == 0 ]; then
    for value_937_vec in ${names_937_vec[@]};
    do
      if [ $1 == $value_937_vec ]; then
	return 0
      fi
    INDEX_937_vec=$(($INDEX_937_vec+1))
    done
  fi
  return 1
}

# FUNCTION
# Name: vec_new()
# Describe: create new vector
# Usage: vec_new name [values]
# Exitcodes: 1 - if name exist
vec_new()
{
   if ! [ $# == 0 ]; then
    if exist_937_vec $1; then
      #echo "Vector name already i use: "$1
      return 1
    fi
    index_937_vec=${#names_937_vec[@]}
    names_937_vec[$index_937_vec]=$1
    next_937_vec[$index_937_vec]="-1"
    len_937_vec[$index_937_vec]=0
    shift 
    string_937_vec=""
    cmax_937_vec=$#
    for (( c_937_vec=1; c_937_vec<=$cmax_937_vec; c_937_vec++ ))
    do
      string_937_vec=$string_937_vec$1$separator_937_vec
      len_937_vec[$index_937_vec]=$((${len_937_vec[$index_937_vec]}+1))
      shift
    done
    vectors_937_vec[$index_937_vec]=$string_937_vec
  fi
  return 0
}

# FUNCTION
# Name: vec_add()
# Describe: add value to existing vector
# Usage: vec_add vector_name value | values
# Exitcodes: 1 - if vector do not exist
vec_add()
{
  if ! [ $# == 0 ]; then
    if exist_937_vec $1; then
      shift
      string_937_vec=${vectors_937_vec[$INDEX_937_vec]}
      cmax_937_vec=$#
      for (( c_937_vec=1; c_937_vec<=$cmax_937_vec; c_937_vec++ ))
      do
	string_937_vec=$string_937_vec$1$separator_937_vec
	len_937_vec[$index_937_vec]=$((${len_937_vec[$index_937_vec]}+1))
	shift
      done
      vectors_937_vec[$INDEX_937_vec]=$string_937_vec
      return 0
    fi
    return 1
  fi
  return 0
}

# FUNCTION
# Name: vec_get()
# Describe: Returns vector value in given position.
#           If [index] position is not given, it returns value currently set by SHIFT function (vec_shift).
#
# Usage: vec_get name [index]
# Exitcodes: 1 if vector do not exist of given index is out of range
vec_get()
{
  if [ $# == 1 ] && exist_937_vec $1; then
    vec_get $1 ${next_937_vec[$INDEX_937_vec]}
    return 0
  fi
  if [ $# == 2 ] && [ $2 == "-1" ]; then
      vec_get $1 0
      return 0
  fi
  compressed_397_vec="$(echo $2 | sed -e 's/[^[:digit:]]//g')"
  if [ $# == 2 ] && ! [ ${#compressed_397_vec} == 0 ] && [ $2 == $compressed_397_vec ]; then
    if exist_937_vec $1; then
      if [ $2 -gt ${len_937_vec[$INDEX_937_vec]} ]; then
	return 1
      fi
      tmp_937_vec="${vectors_937_vec[$INDEX_937_vec]}"
      for ((i_937_vec=0; i_937_vec<$2; i_937_vec++))
      do
	tmp_937_vec=${tmp_937_vec:$(expr index "$tmp_937_vec" $separator_937_vec):${#tmp_937_vec}}
#  	tmp_937_vec="$(echo $tmp_937_vec | sed -e s/".[^$separator_937_vec]*$separator_937_vec"//)"
      done
      echo ${tmp_937_vec:0:$(expr index "$tmp_937_vec" $separator_937_vec)-1}
      return 0
    fi
    return 1
  fi
  return 0
}


# FUNCTION
# Name: vec_getall()
# Describe: Returns all values in vector [name]
# Usage: vec_getall name
# Exitcodes: 1 if vector do not exist
vec_getall()
{
  if [ $# == 1 ]; then
    if ! exist_937_vec $1; then
      return 1
    fi
    tmp_937_vec=${vectors_937_vec[$INDEX_937_vec]}
    if [ ${#tmp_937_vec} == 0 ]; then
      return 1
    fi
    echo $(echo "$tmp_937_vec" | sed -e "s/$separator_937_vec/ /g")
  fi
  return 0
}

# FUNCTION
# Name: vec_len()
# Describe: Returns number of objects in vector [name]
# Usage: vec_len name
# Exitcodes: 1 if vector do not exist
vec_len()
{
  if [ $# == 1 ]; then
    if ! exist_937_vec $1; then
      return 1
    fi
    echo ${len_937_vec[$INDEX_937_vec]}
  fi 
  return 0
}

# FUNCTION
# Name: vec_shift
# Describe: Shift index marker for vec_get function. If marker is set on out of range position exitgode is 1 and marker goes to begin.
# Usage: vec_shift vectorName 
# Exitcodes: 0 - if vector exist and index marker position is not out of range
#	     1 - if index marker position is out of range
#	   255 - if vector doesn't exist
vec_shift()
{
  if [ $# == 1 ]; then
    if ! exist_937_vec $1; then
      return 255
    fi
      next_937_vec[$INDEX_937_vec]=$((${next_937_vec[$INDEX_937_vec]}+1))
    if [ `vec_len $1` == ${next_937_vec[$INDEX_937_vec]} ]; then
      next_937_vec[$INDEX_937_vec]="-1"
      return 1
    fi
    return 0
  fi
}

# FUNCTION
# Name: vec_del()
# Describe: Deleting given element from vector [name]
# Usage: vec_del() vectorName elementIndex
# Exitcodes: 0 - if vector exist and index is not out of range; element has been successfully deleted
#	     1 - if vector doesn't exist or given index is out of range
#	   255 - if incorrect usage
vec_del()
{
  if [ $# == 2 ]; then
    if ! exist_937_vec $1; then
      return 1
    fi
    compressed_397_vec="$(echo $2 | sed -e 's/[^[:digit:]]//g')"
    if [ ${#compressed_397_vec} == 0 ] || ! [ $2 == $compressed_397_vec ]; then
      return 255
    fi
    if [ $2 -gt ${len_937_vec[$INDEX_937_vec]} ]; then
      return 1
    fi
      new_937_vec=""
      tmp_937_vec="${vectors_937_vec[$INDEX_937_vec]}"
      for ((i_937_vec=0; i_937_vec<$2; i_937_vec++))
      do
	new_937_vec="$new_937_vec${tmp_937_vec:0:$(expr index "$tmp_937_vec" $separator_937_vec)}"
	tmp_937_vec=${tmp_937_vec:$(expr index "$tmp_937_vec" $separator_937_vec):${#tmp_937_vec}}
      done
      tmp_937_vec=${tmp_937_vec:$(expr index "$tmp_937_vec" $separator_937_vec):${#tmp_937_vec}}
      new_937_vec="$new_937_vec${tmp_937_vec:0:${#tmp_937_vec}}"
      vectors_937_vec[$INDEX_937_vec]="$new_937_vec"
      len_937_vec[$index_937_vec]=$((${len_937_vec[$index_937_vec]}-1))
    return 0
  fi
  return 255
}

# FUNCTION
# Name: vec_set()
# Describe: seting new value of given element from vector [name]
# Usage: vec_set() vectorName elementIndex newValue
# Exitcodes: 0 - if vector exist and index is not out of range; element has been successfully changed
#	     1 - if vector doesn't exist or given index is out of range
#	   255 - if incorrect usage
vec_set()
{
  if [ $# == 3 ]; then
    if ! exist_937_vec $1; then
      return 1
    fi
    compressed_397_vec="$(echo $2 | sed -e 's/[^[:digit:]]//g')"
    if [ ${#compressed_397_vec} == 0 ] || ! [ $2 == $compressed_397_vec ]; then
      return 255
    fi
    if [ $2 -gt ${len_937_vec[$INDEX_937_vec]} ]; then
      return 1
    fi
    new_937_vec=""
    tmp_937_vec="${vectors_937_vec[$INDEX_937_vec]}"
    for ((i_937_vec=0; i_937_vec<$2; i_937_vec++))
    do
      new_937_vec="$new_937_vec${tmp_937_vec:0:$(expr index "$tmp_937_vec" $separator_937_vec)}"
      tmp_937_vec=${tmp_937_vec:$(expr index "$tmp_937_vec" $separator_937_vec):${#tmp_937_vec}}
    done
    tmp_937_vec=${tmp_937_vec:$(expr index "$tmp_937_vec" $separator_937_vec):${#tmp_937_vec}}
    new_937_vec="$new_937_vec$3$separator_937_vec${tmp_937_vec:0:${#tmp_937_vec}}"
    vectors_937_vec[$INDEX_937_vec]="$new_937_vec"
    return 0
  fi
  return 255
}

# FUNCTION
# Name: vec_clone()
# Describe: Copy all elements between vectors.
# Usage: vec_clone sourceVectorName targetVectorName
# Exitcodes: 0 - if bouth source and target vectors exist 
# 	     1 - if one of the source or target vectors doesn't exist
vec_clone()
{
  if [ $# == 2 ]; then
    if ! exist_937_vec $1; then
      return 1
    fi
    sourceIndex_937_vec=$INDEX_937_vec
     if ! exist_937_vec $2; then
      return 1
    fi
    targetIndex_937_vec=$INDEX_937_vec
  fi
  vectors_937_vec[$targetIndex_937_vec]="${vectors_937_vec[$sourceIndex_937_vec]}"
  len_937_vec[$targetIndex_937_vec]="${len_937_vec[$sourceIndex_937_vec]}"
  return 0
}

# FUNCTION
# Name: vec_getid()
# Describe: Returns position of element in vector
# Usage: vec_getid name element
# Exitcodes: 1 - if vector doesn't contain the element, 
# 	     0 - if contains,
#	   255 - if vector name doesn't exist or wrong usage
vec_getid()
{
    if [ $# == 2 ]; then
      if ! exist_937_vec $1; then
	return 255
      fi
      tmp_937_vec="${vectors_937_vec[$INDEX_937_vec]}"
      for ((i_937_vec=0; i_937_vec<${len_937_vec[$INDEX_937_vec]}; i_937_vec++))
      do
	if [ "${tmp_937_vec:0:$(expr index "$tmp_937_vec" $separator_937_vec)-1}" == "$2" ];then
	    echo $i_937_vec
	    return 0
	fi
	tmp_937_vec=${tmp_937_vec:$(expr index "$tmp_937_vec" $separator_937_vec):${#tmp_937_vec}}
      done
    return 1
    fi
  return 255
}

# FUNCTION
# Name: vec_contain()
# Describe: Check if vector contains element
# Usage: vec_contain name element
# Exitcodes:  1 - if vector doesn't contain the element,
#	      0 - if contains,
#	    255 - if vector name doesn't exist
vec_contain()
{
  if [ $# == 2 ]; then
    if ! exist_937_vec $1; then
      return 255
    fi
    tmp_937_vec="${vectors_937_vec[$INDEX_937_vec]}"
      for ((i_937_vec=0; i_937_vec<${len_937_vec[$INDEX_937_vec]}; i_937_vec++))
      do
	if [ "${tmp_937_vec:0:$(expr index "$tmp_937_vec" $separator_937_vec)-1}" == "$2" ];then
	    return 0
	fi
	tmp_937_vec=${tmp_937_vec:$(expr index "$tmp_937_vec" $separator_937_vec):${#tmp_937_vec}}
      done
    return 1
  fi
  return 255
}
